package calculatorEngine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PassengerMappingAssistant {
	
	/**
	 * Sugeruje najlepsze (z punktu widzenia srodka ciezkosci samolotu) miejsce dla kolejnego pasazera w podanej mapie siedzen.
	 * Jesli zadziala, to sam sie zdziwie :)
	 * 
	 * @param mapaSiedzen Mapa siedzien, dla ktorej szukane jest najlepsze nastepne miejsce. Jesli argument ten bedzie rowny null, prawdopodobnie cos wybuchnie :)
	 * @param klasaSiedzenia Szukane siedzenie ma byc klasy ekonomicznej @link Seat.ECONOMICAL_CLASS czy biznesowej @link Seat.BUSINESS_CLASS. Mozliwe takze niezdefiniowanie tego argumentu, poprzez stala Seat.ANY_CLASS
	 * @param idealnySrodekCiezkosci Wspozedne(X,Y) zadanego srodka ciezkosci samolotu, zazwyczaj X bedzie rowny 0. Note: wspolrzedna Y ROSNIE w kierunku ogona samolotu!
	 * @return Koordynaty sugerowanego siedzienia w podanej mapie, podane w kolejnosci (x, y), sugerowane odwolanie do wskazanego siedzienia: arrayList.get(y)[x]
	 */
	 
	
	public static int[] suggestSeat(final ArrayList<Seat[]> mapaSiedzen, int klasaSiedzenia, double[] idealnySrodekCiezkosci) {
		
////////SEKCJA I - tworzenie listy wolnych siedzen
		ArrayList<int[]> listaWolnychSiedzen = new ArrayList<int[]>();
		
		int iloscSiedzenWRzedzie = mapaSiedzen.get(0).length;
		int iloscRzedow = mapaSiedzen.size();
		
		for (int y = 0; y < mapaSiedzen.size(); y++) {
			
			for (int x = 0; x < iloscSiedzenWRzedzie; x++) {
				
				if (mapaSiedzen.get(y)[x].getStatus() == Seat.FREE) { 
					
					if (mapaSiedzen.get(y)[x].getComfortClass() == klasaSiedzenia || klasaSiedzenia == Seat.ANY_CLASS) {
					
						listaWolnychSiedzen.add(new int[] {x,y});
					
						//System.out.println("Wolne siedzenie: " + Integer.toString(x) + ", " + Integer.toString(y));
					
					}
					
				}
				
			}
			
		}
		
		/*int takenSeats1 = 0;
		
		for (int i1 = 0; i1 < mapaSiedzen.size(); i1++) {
			
			for (int i2 = 0; i2 < mapaSiedzen.get(0).length; i2++) {
				
				if (mapaSiedzen.get(i1)[i2].getStatus() == Seat.TAKEN) {
					
					takenSeats1++;
					
				}
				
			}
			
		}
		
		System.out.println("ZAJETE MIESJCA PMA_RAW: " + Integer.toString(takenSeats1));*/

		
////////SEKCJA II - sprawdzanie srodka ciezkosci w kazdym mozliwym przypadku rozmieszczenia pasazera
		
		ArrayList<double[]> listaWywazen = new ArrayList<double[]>();
		
		for (int i = 0; i < listaWolnychSiedzen.size(); i++) {
			
			int xSiedzenia = listaWolnychSiedzen.get(i)[0];
			int ySiedzenia = listaWolnychSiedzen.get(i)[1];
			
			int takenSeats = 0;
			
			//System.out.println("WOLNE MIESJCE SPRAWDZANE(" + Integer.toString(xSiedzenia) + "," + Integer.toString(ySiedzenia) + ")");
						
			if (mapaSiedzen.get(ySiedzenia)[xSiedzenia].getStatus() == Seat.TAKEN) {
				
				System.out.println("LETS COMMIT..................... SUICIDE");
				
			}
			
			//mapaSiedzen.get(ySiedzenia)[xSiedzenia].setStatus(Seat.TAKEN);
						
			Seat siedzenieDoPodmiany = new Seat(Seat.TAKEN, mapaSiedzen.get(ySiedzenia)[xSiedzenia]);
			
			//The evilest thing in the whole universe, zmienna siedzenieDoPodmiany staje sie KAZDYM SIEDZENIEM NARAZ
			//Seat siedzenieDoPodmiany = mapaSiedzen.get(ySiedzenia)[xSiedzenia];
			
			//ten statement z jakiegos powodu zmienia status wszystkich siedzen na Seat.TAKEN
			siedzenieDoPodmiany.setStatus(Seat.TAKEN);
			
			/*for (int i1 = 0; i1 < mapaSiedzen.size(); i1++) {
				
				for (int i2 = 0; i2 < mapaSiedzen.get(0).length; i2++) {
					
					System.out.println("STATUS:" + Integer.toString(mapaSiedzen.get(i1)[i2].getStatus()));
					
					if (mapaSiedzen.get(i1)[i2].getStatus() == Seat.TAKEN) {
						
						takenSeats++;
						
					}
					
				}
				
			}*/
			
			//System.out.println("ZAJETE MIESJCA PMA: " + Integer.toString(takenSeats));
						
			mapaSiedzen.get(ySiedzenia)[xSiedzenia] = siedzenieDoPodmiany;
			
			listaWywazen.add(CenterWeightProcessor.getWeightCenter(mapaSiedzen));
			
			siedzenieDoPodmiany.setStatus(Seat.FREE);
			
			mapaSiedzen.get(ySiedzenia)[xSiedzenia] = siedzenieDoPodmiany;
			
		}
		
		return PassengerMappingAssistant.wybierzNaPodstawieNajlepszegoPrzesunieciaWektorowego(mapaSiedzen, listaWywazen, listaWolnychSiedzen, idealnySrodekCiezkosci, iloscRzedow, iloscSiedzenWRzedzie);
		
	}
	
	public static int[] suggestSeatMakeThemHappy(final ArrayList<Seat[]> mapaSiedzen, int klasaSiedzenia, String preferencja, double[] idealnySrodekCiezkosci) {
		
	if (preferencja.equals(Seat.NONE)) {System.out.println("NOTWILLINGTOBEHAPPY"); int[] x = suggestSeat(mapaSiedzen, klasaSiedzenia, idealnySrodekCiezkosci); System.out.println(Integer.toString(x[0]) + " " + Integer.toString(x[1])); return x; }
	else if (CenterWeightProcessor.getPercentageBalance(mapaSiedzen, idealnySrodekCiezkosci) <= 0.8 && !Utils.mniejNiz10ProcentZajete(mapaSiedzen)) {System.out.println("NEVERHAPPY_ALIVE"); int[] x = suggestSeat(mapaSiedzen, klasaSiedzenia, idealnySrodekCiezkosci); System.out.println(Integer.toString(x[0]) + " " + Integer.toString(x[1])); return x; }	

	////////SEKCJA I - tworzenie listy wolnych siedzen
		ArrayList<int[]> listaWolnychSiedzen = new ArrayList<int[]>();
		
		int iloscSiedzenWRzedzie = mapaSiedzen.get(0).length;
		int iloscRzedow = mapaSiedzen.size();
		
		for (int y = 0; y < mapaSiedzen.size(); y++) {
			
			for (int x = 0; x < iloscSiedzenWRzedzie; x++) {
				
				if (mapaSiedzen.get(y)[x].getStatus() == Seat.FREE) { 
					
					if(mapaSiedzen.get(y)[x].getComfortClass() == klasaSiedzenia || klasaSiedzenia == Seat.ANY_CLASS) {
						
						//System.out.println(mapaSiedzen.get(y)[x].getPreferences());
					
						if (mapaSiedzen.get(y)[x].getPreferences().equals(preferencja)) listaWolnychSiedzen.add(new int[] {x,y});
					
						//System.out.println("Wolne siedzenie: " + Integer.toString(x) + ", " + Integer.toString(y));
					
					}
					
				}
				
			}
			
		}
		
//		System.out.println("ILOSC WOLNYCH SIEDZEN " + preferencja + " = " + Integer.toString(listaWolnychSiedzen.size()));
		
		/*int takenSeats1 = 0;
		
		for (int i1 = 0; i1 < mapaSiedzen.size(); i1++) {
			
			for (int i2 = 0; i2 < mapaSiedzen.get(0).length; i2++) {
				
				if (mapaSiedzen.get(i1)[i2].getStatus() == Seat.TAKEN) {
					
					takenSeats1++;
					
				}
				
			}
			
		}
		
		System.out.println("ZAJETE MIESJCA PMA_RAW: " + Integer.toString(takenSeats1));*/

		
////////SEKCJA II - sprawdzanie srodka ciezkosci w kazdym mozliwym przypadku rozmieszczenia pasazera
		
		ArrayList<double[]> listaWywazen = new ArrayList<double[]>();
		
		for (int i = 0; i < listaWolnychSiedzen.size(); i++) {
			
			int xSiedzenia = listaWolnychSiedzen.get(i)[0];
			int ySiedzenia = listaWolnychSiedzen.get(i)[1];
			
			int takenSeats = 0;
			
			//System.out.println("WOLNE MIESJCE SPRAWDZANE(" + Integer.toString(xSiedzenia) + "," + Integer.toString(ySiedzenia) + ")");
						
			if (mapaSiedzen.get(ySiedzenia)[xSiedzenia].getStatus() == Seat.TAKEN) {
				
				System.out.println("LETS COMMIT..................... SUICIDE");
				
			}
			
			//mapaSiedzen.get(ySiedzenia)[xSiedzenia].setStatus(Seat.TAKEN);
						
			Seat siedzenieDoPodmiany = new Seat(Seat.TAKEN, mapaSiedzen.get(ySiedzenia)[xSiedzenia]);
			
			//The evilest thing in the whole universe, zmienna siedzenieDoPodmiany staje sie KAZDYM SIEDZENIEM NARAZ
			//Seat siedzenieDoPodmiany = mapaSiedzen.get(ySiedzenia)[xSiedzenia];
			
			//ten statement z jakiegos powodu zmienia status wszystkich siedzen na Seat.TAKEN
			siedzenieDoPodmiany.setStatus(Seat.TAKEN);
			
			/*for (int i1 = 0; i1 < mapaSiedzen.size(); i1++) {
				
				for (int i2 = 0; i2 < mapaSiedzen.get(0).length; i2++) {
					
					System.out.println("STATUS:" + Integer.toString(mapaSiedzen.get(i1)[i2].getStatus()));
					
					if (mapaSiedzen.get(i1)[i2].getStatus() == Seat.TAKEN) {
						
						takenSeats++;
						
					}
					
				}
				
			}*/
			
			//System.out.println("ZAJETE MIESJCA PMA: " + Integer.toString(takenSeats));
						
			mapaSiedzen.get(ySiedzenia)[xSiedzenia] = siedzenieDoPodmiany;
			
			listaWywazen.add(CenterWeightProcessor.getWeightCenter(mapaSiedzen));
			
			siedzenieDoPodmiany.setStatus(Seat.FREE);
			
			mapaSiedzen.get(ySiedzenia)[xSiedzenia] = siedzenieDoPodmiany;
			
		}
		
		int[] wynik = PassengerMappingAssistant.wybierzNaPodstawieNajlepszegoPrzesunieciaWektorowego(mapaSiedzen, listaWywazen, listaWolnychSiedzen, idealnySrodekCiezkosci, iloscRzedow, iloscSiedzenWRzedzie);
		
		if (wynik[0] == -1) { System.out.println("NOFUCKINGSEAT"); return suggestSeat(mapaSiedzen, klasaSiedzenia, idealnySrodekCiezkosci); }
		else { System.out.println("HAPPY"); return wynik; }
		
	}
	
	/**
	 * Wybiera najlepsze miejsce biorac pod uwage srodek ciezkosci przesuwajacy sie po osi X
	 * @param listaWywazen
	 * @param listaWolnychSiedzen
	 * @return
	 */
	private static int[] wybierzNaPodstawieNajlepszegoX(final ArrayList<double[]> listaWywazen, final ArrayList<int[]> listaWolnychSiedzen) {
		
		ArrayList<Double> listaX = new ArrayList<Double>();
		
		for (int i = 0; i < listaWywazen.size(); i++) {
			
			//lista deltaX z wywazen
			listaX.add(Math.abs(listaWywazen.get(i)[0]));
			
		}
		
		ArrayList<LiczbaZIndexem> listaDeltaXZIndexem = new ArrayList<LiczbaZIndexem>();
		
		for (int i = 0; i < listaX.size(); i++) {
			
			listaDeltaXZIndexem.add(new LiczbaZIndexem(i, listaX.get(i)));
			System.out.println("INDEX + DELTAX:" + Integer.toString(i) + ", " + Double.toString(listaX.get(i)));
			
		}
		
		//sortuje w kolejnosci od najblizszej 0 do najdalej od 0
		Collections.sort(listaDeltaXZIndexem);
		
		for (int i = 0; i < listaDeltaXZIndexem.size(); i++) {
			
			//System.out.println(listaDeltaXZIndexem.get(i).liczba);
			
		}
		
		try {
			
			return listaWolnychSiedzen.get(listaDeltaXZIndexem.get(0).index);
		}
		
		catch (IndexOutOfBoundsException e) {
			
			return new int[] {-1, -1};
			
		}
		
	}
	
	/**
	 * Wybiera najlepsze miejsce biorac pod uwage srodek ciezkosci przesuwajacy sie po osi Y
	 * @param listaWywazen
	 * @param listaWolnychSiedzen
	 * @param najlepszySrodekCiezkosci
	 * @return
	 */
	private static int[] wybierzNaPodstawieNajlepszegoY(final ArrayList<double[]> listaWywazen, final ArrayList<int[]> listaWolnychSiedzen, double najlepszySrodekCiezkosci) {
		
		ArrayList<Double> listaY = new ArrayList<Double>();
		
		for (int i = 0; i < listaWywazen.size(); i++) {
			
			listaY.add(listaWywazen.get(i)[1]);
			
		}
		
		ArrayList<LiczbaZIndexem> listaDeltaYZIndexem = new ArrayList<LiczbaZIndexem>();
		
		for (int i = 0; i < listaY.size(); i++) {
			
			listaDeltaYZIndexem.add(new LiczbaZIndexem(i, listaY.get(i)));
			
		}
		
		for (int i = 0; i < listaY.size(); i++) {
			
			listaDeltaYZIndexem.add(new LiczbaZIndexem(i, listaY.get(i)));
			System.out.println("INDEX + DELTAY:" + Integer.toString(i) + ", " + Double.toString(listaY.get(i)));
			
		}
		
		Collections.sort(listaDeltaYZIndexem);
		
		try {
			
			return listaWolnychSiedzen.get(listaDeltaYZIndexem.get(0).index);
		}
		
		catch (IndexOutOfBoundsException e) {
			
			return new int[] {-1, -1};
			
		}
		
	}
	
	
	private static int[] wybierzNaPodstawieNajlepszegoPrzesunieciaWektorowego(final ArrayList<Seat[]> mapaSiedzen, final ArrayList<double[]> listaWywazen, final ArrayList<int[]> listaWolnychSiedzen, final double[] idealnySrodekCiezkosci, int iloscRzedow, int iloscSiedzenWRzedzie) {
		
		double xIdealnySC = idealnySrodekCiezkosci[0];
		double yIdealnySC = idealnySrodekCiezkosci[1];
		
		int liczbaRzedowY = iloscRzedow;
		int liczbaSiedzenWRzedzieX = iloscSiedzenWRzedzie;
		
		ArrayList<LiczbaZIndexem> listaWywazenWektorowych = new ArrayList<LiczbaZIndexem>();
		
		for (int i = 0; i < listaWywazen.size(); i++) {
			
			double noweWywazenieX = Math.pow(Math.abs(xIdealnySC - listaWywazen.get(i)[0]) * liczbaRzedowY, 2);
			double noweWywazenieY = Math.pow(Math.abs(Math.abs(yIdealnySC - listaWywazen.get(i)[1])*liczbaSiedzenWRzedzieX), 2);
			
			double noweWywazenieWektorowe = Math.sqrt(noweWywazenieX + noweWywazenieY);
			
			listaWywazenWektorowych.add(new LiczbaZIndexem(i, noweWywazenieWektorowe));
			
		}
		
		Collections.sort(listaWywazenWektorowych);
		
		try {
			
			int szczesliwyIndex = listaWywazenWektorowych.get(0).index;
			
			return new int[] { listaWolnychSiedzen.get(szczesliwyIndex)[0], listaWolnychSiedzen.get(szczesliwyIndex)[1] };
		
		}
		
		catch (IndexOutOfBoundsException e) {
			
			return new int[] {-1, -1};
			
		}
				
	}
	
	//powinno dzialac
	public static double getPercentageHappiness(List<Seat> mapaSiedzen) {
		
		int liczba_pasazerow = 0;
		int liczba_zadowolonych_pasazerow = 0;
		
		for (int y = 0; y < mapaSiedzen.size(); y++) {
			
							
				if (mapaSiedzen.get(y).getStatus() != Seat.FREE && mapaSiedzen.get(y).getStatus() != Seat.OUT_OF_ORDER){ 
					
					liczba_pasazerow++;
					}
					if (mapaSiedzen.get(y).isHappy() == Seat.HAPPY) {
						
						liczba_zadowolonych_pasazerow++;
						
					}
					
				
				
			
			
		}
		
		return (double)liczba_zadowolonych_pasazerow / (double)liczba_pasazerow;
		
	}
	
	//klasa pomocnicza pomagajaca ustalic najlepsze wywazenie samolotu
	public static class LiczbaZIndexem implements Comparable<LiczbaZIndexem> {
		
		public Double liczba;
		public Integer index;
		
		public LiczbaZIndexem(int index, double liczba) {
			// TODO Auto-generated constructor stub
			this.liczba = liczba;
			this.index = index;
			
		}

		@Override
		public int compareTo(LiczbaZIndexem o) {
			// TODO Auto-generated method stub
			return this.liczba.compareTo(o.liczba);
			
		}
		
	}

}
